计算yolov5中detect.py生成图像的mAP

您所在的位置:网站首页 yolo 输出的结果图 计算yolov5中detect.py生成图像的mAP

计算yolov5中detect.py生成图像的mAP

2024-07-04 00:57| 来源: 网络整理| 查看: 265

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录 前言一、用YoloV5的detect.py生成预测图,预测类别,预测框坐标,预测置信度1、跑detect.py程序 二、将测试图的Annotation的XML文件转化为txt文件,使用yolo坐标格式表示三、将YoloV5和GroundTruth的yolo坐标转换为voc坐标1.将groundtruth改成voc坐标:2.将yolo结果改成voc坐标: 四、对某些应yolov5未检测到物体而不生成的txt文件,去除groundTruth中对应文件,然后测试mAP

前言

本文适用背景:

准备直接用yolov5提供的预训练模型coco128测试自己图像,而不是自己训练模型,然后进行目标检测。准备对自己测试后的图像进行计算mAP等指标。

本文参考了许多文章,从中拷贝部分程序。如有侵权问题,请及时联系我,我会进行删改。

提示:以下是本篇文章正文内容,下面案例可供参考

一、用YoloV5的detect.py生成预测图,预测类别,预测框坐标,预测置信度

下载yolov5程序包,安装好环境,下载好权重文件放在weights文件夹这种就不说了,不会的自己百度。

1、跑detect.py程序

yolov5中主要有三个程序,train.py,val.py,detect.py。一般正常的训练自己数据集,然后测试。就用到train.py和val.py。但谁让我们不正常呢?我们只是用他的预训练模型来测试自己的图片。所以,首先要保证,你要测试的东西是他的预训练模型能测得到的。coco128.yaml的测试范围如下: 超出这个范围还是老老实实自己训练吧 如果你的类别在里面,那恭喜进入下一步。 在这里插入图片描述 这么多超参数,你其实要改的不多,第一个weights是权重文件的路径。source是你的原图地址。classes是你在上面那么多类别中选择的类别,比如你的图片有人和车,但你的Annotation中只有人这一个类别,你直接跑的化,会输出车和人的预测结果,但你只要人的结果,你就可以选择–classes 0,表示只检测人。比如我的测试集要测试的类别为: 在这里插入图片描述 那对应到coco.yaml中就是 0 2 3 5 7 9,这个数字不一定要从小到大,它只是个标签。 然后–save-txt就是把预测类别,框坐标存在txt文件中。yolo的坐标格式为:

–save-conf 就是加上置信度,变成:

所以,我的代码最后再终端运行的是:

python detect.py --source /home/test/Mytest --weights weights/yolov5l.pt --classes 0 2 3 5 7 9 --save-txt --save-conf

然后会在runs/detect/exp中生成你要的图片和文件。

上面是训练的基础,但我们需要修改代码。因为大家也看到了coco.yaml记录了图片的类别和标签,即person:0,car:2,bus:5。但我们的测试集的类别是people:0,bus:1, car:2.怎么办呢? 直接上代码:

# Write results for *xyxy, conf, cls in reversed(det): # print(cls) if cls ==torch.tensor(0., device='cuda:0'):cls = torch.tensor(0., device='cuda:0') if cls ==torch.tensor(5., device='cuda:0'):cls =torch.tensor(1., device='cuda:0') if cls ==torch.tensor(2., device='cuda:0'):cls =torch.tensor(2., device='cuda:0') if cls ==torch.tensor(3., device='cuda:0'):cls =torch.tensor(3., device='cuda:0') if cls ==torch.tensor(9., device='cuda:0'):cls =torch.tensor(4., device='cuda:0') if cls ==torch.tensor(7., device='cuda:0'):cls =torch.tensor(5., device='cuda:0') if save_txt: # Write to file xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh line = (cls, *xywh, conf) if save_conf else (cls, *xywh) # label format with open(txt_path + '.txt', 'a') as f: f.write(('%g ' * len(line)).rstrip() % line + '\n') if save_img or save_crop or view_img: # Add bbox to image if cls ==torch.tensor(0., device='cuda:0'):cls = torch.tensor(0., device='cuda:0') if cls ==torch.tensor(1., device='cuda:0'):cls =torch.tensor(5., device='cuda:0') if cls ==torch.tensor(2., device='cuda:0'):cls =torch.tensor(2., device='cuda:0') if cls ==torch.tensor(3., device='cuda:0'):cls =torch.tensor(3., device='cuda:0') if cls ==torch.tensor(4., device='cuda:0'):cls =torch.tensor(9., device='cuda:0') if cls ==torch.tensor(5., device='cuda:0'):cls =torch.tensor(7., device='cuda:0') c = int(cls) # integer class label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}') annotator.box_label(xyxy, label, color=colors(c, True)) if save_crop: save_one_box(xyxy, imc, file=save_dir / 'crops' / names[c] / f'{p.stem}.jpg', BGR=True)

去detect.py中找到上述代码段,加上我改的那些个if语句.也就是模型检测到一辆bus,他给出标签为5,那我们就在存储到txt前将5改为1.总共两段if代码段,前一段是改txt的,后一段是改回来,避免给图片写名字时写错. 这样,我们就获得了yolov5的预测结果. 如果你仔细看生成的txt文件,你会发现部分图片没有生成txt,这是因为模型没在你那张图片上检测到物体,他就不生成txt文件,txt文件名会和图片名一致

二、将测试图的Annotation的XML文件转化为txt文件,使用yolo坐标格式表示

如何将我们测试图的GroundTruth标签从xml转换成txt(这边xml名字要和前面图片名一致) 直接上代码: voc_label.py

# -*- coding: utf-8 -*- # xml解析包 import xml.etree.ElementTree as ET import os from os import getcwd import shutil sets = ['test_m3df'] classes = ['People', 'Bus', 'Car', 'Motorcycle', 'Lamp', 'Truck'] style = '.png' # 进行归一化操作 def convert(size, box): # size:(原图w,原图h) , box:(xmin,xmax,ymin,ymax) dw = 1. / size[0] # 1/w dh = 1. / size[1] # 1/h x = (box[0] + box[1]) / 2.0 # 物体在图中的中心点x坐标 y = (box[2] + box[3]) / 2.0 # 物体在图中的中心点y坐标 w = box[1] - box[0] # 物体实际像素宽度 h = box[3] - box[2] # 物体实际像素高度 x = x * dw # 物体中心点x的坐标比(相当于 x/原图w) w = w * dw # 物体宽度的宽度比(相当于 w/原图w) y = y * dh # 物体中心点y的坐标比(相当于 y/原图h) h = h * dh # 物体宽度的宽度比(相当于 h/原图h) return (x, y, w, h) # 返回 相对于原图的物体中心点的x坐标比,y坐标比,宽度比,高度比,取值范围[0-1] # year ='2012', 对应图片的id(文件名) def convert_annotation(image_id): ''' 将对应文件名的xml文件转化为label文件,xml文件包含了对应的bunding框以及图片长款大小等信息, 通过对其解析,然后进行归一化最终读到label文件中去,也就是说 一张图片文件对应一个xml文件,然后通过解析和归一化,能够将对应的信息保存到唯一一个label文件中去 labal文件中的格式:calss x y w h  同时,一张图片对应的类别有多个,所以对应的bunding的信息也有多个 ''' # 对应的通过year 找到相应的文件夹,并且打开相应image_id的xml文件,其对应bund文件 in_file = open('./data/Annotations/%s.xml' % (image_id), encoding='utf-8') # 准备在对应的image_id 中写入对应的label,分别为 # out_file = open('./data/labels/%s.txt' % (image_id), 'w', encoding='utf-8') # 解析xml文件 tree = ET.parse(in_file) # 获得对应的键值对 root = tree.getroot() # 获得图片的尺寸大小 size = root.find('size') # 如果xml内的标记为空,增加判断条件 if size != None: # 获得宽 w = int(size.find('width').text) # 获得高 h = int(size.find('height').text) # 遍历目标obj for obj in root.iter('object'): # 获得difficult ?? difficult = obj.find('difficult').text # 获得类别 =string 类型 cls = obj.find('name').text # 如果类别不是对应在我们预定好的class文件中,或difficult==1则跳过 if cls not in classes or int(difficult) == 1: continue # 通过类别名称找到id cls_id = classes.index(cls) # 找到bndbox 对象 xmlbox = obj.find('bndbox') # 获取对应的bndbox的数组 = ['xmin','xmax','ymin','ymax'] b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text)) print(image_id, cls, b) # 带入进行归一化操作 # w = 宽, h = 高, b= bndbox的数组 = ['xmin','xmax','ymin','ymax'] bb = convert((w, h), b) # bb 对应的是归一化后的(x,y,w,h) # 生成 calss x y w h 在label文件中 out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n') # 返回当前工作目录 wd = getcwd() print(wd) # 先找labels文件夹如果不存在则创建 labels = './data/labels' if os.path.exists(labels): shutil.rmtree(labels) # delete output folder os.makedirs(labels) # make new output folder for image_set in sets: ''' 对所有的文件数据集进行遍历 做了两个工作:     1.将所有图片文件都遍历一遍,并且将其所有的全路径都写在对应的txt文件中去,方便定位     2.同时对所有的图片文件进行解析和转化,将其对应的bundingbox 以及类别的信息全部解析写到label 文件中去      最后再通过直接读取文件,就能找到对应的label 信息 ''' # 读取在ImageSets/Main 中的train、test..等文件的内容 # 包含对应的文件名称 # image_ids = open('./data/ImageSets/%s.txt' % (image_set)).read().strip().split() image_ids = os.listdir('图片地址') image_id_s = [] for i in image_ids: file_name=i.split('.')[0] image_id_s.append(file_name) # 打开对应的2012_train.txt 文件对其进行写入准备 txt_name = './data/%s.txt' % (image_set) if os.path.exists(txt_name): os.remove(txt_name) else: open(txt_name, 'w') list_file = open(txt_name, 'w') # 将对应的文件_id以及全路径写进去并换行 for image_id in image_id_s: list_file.write('data/images/%s%s\n' % (image_id, style)) # 调用 year = 年份 image_id = 对应的文件名_id convert_annotation(image_id) # 关闭文件 list_file.close()

参考该文:https://blog.csdn.net/weixin_42182534/article/details/123608276 这样我们就获得了标签的yolo格式坐标的txt

三、将YoloV5和GroundTruth的yolo坐标转换为voc坐标 1.将groundtruth改成voc坐标:

直接上代码: get_GT.py

import numpy as np import cv2 import torch import os label_path = './test_label' image_path = './test' # 坐标转换,原始存储的是YOLOv5格式 # Convert nx4 boxes from [x, y, w, h] normalized to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right def xywhn2xyxy(x, w=800, h=800, padw=0, padh=0): y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) y[:, 0] = w * (x[:, 0] - x[:, 2] / 2) + padw # top left x y[:, 1] = h * (x[:, 1] - x[:, 3] / 2) + padh # top left y y[:, 2] = w * (x[:, 0] + x[:, 2] / 2) + padw # bottom right x y[:, 3] = h * (x[:, 1] + x[:, 3] / 2) + padh # bottom right y return y folder = os.path.exists('GT') if not folder: os.makedirs('GT') folderlist = os.listdir(label_path) for i in folderlist: label_path_new = os.path.join(label_path, i) with open(label_path_new, 'r') as f: lb = np.array([x.split() for x in f.read().strip().splitlines()], dtype=np.float32) # predict_label read_label = label_path_new.replace(".txt", ".png") read_label_path = read_label.replace('test_label', 'test') print(read_label_path) img = cv2.imread(str(read_label_path)) h, w = img.shape[:2] lb[:, 1:] = xywhn2xyxy(lb[:, 1:], w, h, 0, 0) # 反归一化 for _, x in enumerate(lb): class_label = int(x[0]) # class cv2.rectangle(img, (round(x[1]), round(x[2])), (round(x[3]), round(x[4])), (0, 255, 0)) with open('GT/' + i, 'a') as fw: fw.write(str(x[0]) + ' ' + str(x[1]) + ' ' + str(x[2]) + ' ' + str(x[3]) + ' ' + str( x[4]) + '\n') 2.将yolo结果改成voc坐标:

get_DR.py

import numpy as np import cv2 import torch import os label_path = './predict_label' image_path = './test' # 坐标转换,原始存储的是YOLOv5格式 # Convert nx4 boxes from [x, y, w, h] normalized to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right def xywhn2xyxy(x, w=800, h=800, padw=0, padh=0): y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) y[:, 0] = w * (x[:, 0] - x[:, 2] / 2) + padw # top left x y[:, 1] = h * (x[:, 1] - x[:, 3] / 2) + padh # top left y y[:, 2] = w * (x[:, 0] + x[:, 2] / 2) + padw # bottom right x y[:, 3] = h * (x[:, 1] + x[:, 3] / 2) + padh # bottom right y return y folder = os.path.exists('DR') if not folder: os.makedirs('DR') folderlist = os.listdir(label_path) for i in folderlist: label_path_new = os.path.join(label_path, i) with open(label_path_new, 'r') as f: lb = np.array([x.split() for x in f.read().strip().splitlines()], dtype=np.float32) # predict_label # print(lb) read_label = label_path_new.replace(".txt", ".png") read_label_path = read_label.replace('predict_label', 'test') img = cv2.imread(str(read_label_path)) h, w = img.shape[:2] lb[:, 1:] = xywhn2xyxy(lb[:, 1:], w, h, 0, 0) # 反归一化 # 绘图 for _, x in enumerate(lb): class_label = int(x[0]) # class cv2.rectangle(img, (round(x[1]), round(x[2])), (round(x[3]), round(x[4])), (0, 255, 0)) cv2.putText(img, str(class_label), (int(x[1]), int(x[2] - 2)), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(0, 0, 255), thickness=2) with open('DR/' + i, 'a') as fw: # 这里需要把confidence放到第二位 # print(fw) fw.write(str(x[0]) + ' ' + str(x[5]) + ' ' + str(x[1]) + ' ' + str(x[2]) + ' ' + str(x[3]) + ' ' + str( x[4]) + '\n') # cv2.imshow('show', img) # cv2.waitKey(0) # 按键结束 # cv2.destroyAllWindows()

此处参考:https://blog.csdn.net/qq_38412266/article/details/119559719 不过他的两个get代码在最后第两行,'0'写错了,改成str(x[0]),然后 cv2.rectangle要输入为整数.直接用我的就行

四、对某些应yolov5未检测到物体而不生成的txt文件,去除groundTruth中对应文件,然后测试mAP

这边直接用: get_map.py

import glob import json import os import shutil import operator import sys import argparse import math import numpy as np MINOVERLAP = 0.4 parser = argparse.ArgumentParser() parser.add_argument('-na', '--no-animation', help="no animation is shown.", action="store_true") parser.add_argument('-np', '--no-plot', help="no plot is shown.", action="store_true") parser.add_argument('-q', '--quiet', help="minimalistic console output.", action="store_true") parser.add_argument('-i', '--ignore', nargs='+', type=str, help="ignore a list of classes.") parser.add_argument('--set-class-iou', nargs='+', type=str, help="set IoU for a specific class.") args = parser.parse_args() ''' 0,0 ------> x (width) | | (Left,Top) | *_________ | | | | | y |_________| (height) * (Right,Bottom) ''' if args.ignore is None: args.ignore = [] specific_iou_flagged = False if args.set_class_iou is not None: specific_iou_flagged = True os.chdir(os.path.dirname(os.path.abspath(__file__))) GT_PATH = './GT' DR_PATH = './DR' IMG_PATH = './test' if os.path.exists(IMG_PATH): for dirpath, dirnames, files in os.walk(IMG_PATH): if not files: args.no_animation = True else: args.no_animation = True show_animation = False if not args.no_animation: try: import cv2 show_animation = True except ImportError: print("\"opencv-python\" not found, please install to visualize the results.") args.no_animation = True draw_plot = False if not args.no_plot: try: import matplotlib.pyplot as plt draw_plot = True except ImportError: print("\"matplotlib\" not found, please install it to get the resulting plots.") args.no_plot = True def log_average_miss_rate(precision, fp_cumsum, num_images): """ log-average miss rate: Calculated by averaging miss rates at 9 evenly spaced FPPI points between 10e-2 and 10e0, in log-space. output: lamr | log-average miss rate mr | miss rate fppi | false positives per image references: [1] Dollar, Piotr, et al. "Pedestrian Detection: An Evaluation of the State of the Art." Pattern Analysis and Machine Intelligence, IEEE Transactions on 34.4 (2012): 743 - 761. """ if precision.size == 0: lamr = 0 mr = 1 fppi = 0 return lamr, mr, fppi fppi = fp_cumsum / float(num_images) mr = (1 - precision) fppi_tmp = np.insert(fppi, 0, -1.0) mr_tmp = np.insert(mr, 0, 1.0) ref = np.logspace(-2.0, 0.0, num=9) for i, ref_i in enumerate(ref): j = np.where(fppi_tmp 0.0 and val


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3